"Nonpareil Nonagon" "Decagonal Delight"} 'pies' STO
Next, he writes a little program that takes pie orders. A fragment
that gets the user's choice of pie looks like this:
"Select a Pie:" pies BROWSE 1 GET
This invokes the browser:
Select a pie:
>Triangular Treacle
Raspberry Rhombus
Pentagonal Praline
Hexagon Hash
Strawberry Septagon
##############------->
After the user chooses a pie, the 1 GET retrieves the description of
the user's chosen pie. Peter's program could also add the choice to
an order list or use the position of the choice (2 GET) to look up
pricing information from a price list.
ADVANCED USE OF THE BROWSER
If you wish to have the browser start at a particular position in the
list or if you need to specially format your list items for display,
there is an alternate way to call the browser:
Level 2: a list consisting of:
- Your message (string)
- Current item in list (real number)
- Lines off-screen above the list pane (real number)
- Procedure used to format your list items (program)
Level 1: the list to be browsed (list or name)
The list in level 2 is the same format as that returned to level 2
when the browser ends. This is a brief run-down of its parts:
1) Your message to the user
2) The current item (the list position pointed to by the cursor).
3) The lines off-screen tells the browser how far down in the
list its window should start (if the current item is not visible
in this area, the list pane will be moved far enough in the
appropriate direction to include it).
4) The browser will run your program object on each list item
prior to displaying it. Your program object should take an item
(extracted from your list by the browser and placed in level 1)
and format it for display by the browser. The browser will call
this code every time it draws an item in the display so keep it
fast and simple.
This approach is very powerful because you can start the browser in
any position and format its items any way you like. The list is
returned to you (updated to match the user's actions) when the browser
ends. This enables you to take the user's response, carry out
appropriate actions and return to browsing the list in the same
position the user left off.
One use of this is to browse hierarchically, that is, browse a list of
lists, returning to the proper place every time the user pops up a
level. To do this, just keep a list containing your list path and a
matching list containing browser state lists. When the users pops up
a level, just discard the last item in each list and call the browser
with the new last item. For example a program for selecting people to
receive mailings might work like this:
Select a company:
Apple
Compaq
>Hewlett-Packard User selects company, presses enter
IBM
Zenith
######################
Select a division:
Boise
>Corvallis
Phoenix User selects division, presses enter
Palo Alto
Santa Clara
<-###############---->
Select an Employee:
Sanders, John
Terpack, Daniel
Vogel, Eric User selects employee (presses enter)
West, Carmen (repeat as needed then press backspace)
>Wickes, William
<-------------------#>
Select a division:
Boise
>Corvallis
Phoenix User is returned to the division browser
Palo Alto (with list pane and current item unchanged)
Santa Clara
<-###############---->
TECHNICAL DETAILS OF THE BROWSER
The original version of the browser was a collection of three main
program objects and about two dozen state variables and key handling
objects. To eliminate the need for a separate browser directory, I
have compressed the whole thing into a single program object (with
considerable loss of readability).
The browser has four logical parts:
Browse - sets things up for browsing
MainLoop - waits for key events and hands them off to handlers.
DrawView - draws the browser after key events change it.
Key handlers - interpret the meaning of various keys.
BROWSE
This initialization code is run when the browser is called. It
extracts the input parameters and puts them in temporary variables.
It also calculates the size of the thumb. When Browse finishes setup,
it flows into the main loop.
MAINLOOP
The main loop is wrapped around the DrawView procedure and the key
handlers. It loops, drawing the browser and handling keys until the
user accepts an item or aborts the browsing process.
DRAWVIEW
DrawView checks the current state of the browser and uses one of three
methods to draw the browser's visible parts on the screen.
If the list pane is above the current item (the user scrolled down) it
redraws the pane from the bottom up.
If the list pane is below the current item (the user scrolled up) it
redraws the pane from the top down.
If the current and previous items are both in the pane (the user moved
the cursor within the visible region) only the current and previous
items are redrawn.
Originally, I simply drew the lines of the browser from the top down,
every time. Performance was so slow that when scrolling down, the
display gave the impression that it was scrolling up. This came about
because the user pressed the down arrow key but saw the top lines of
the display changing first. To remedy this, I determine which way the
user scrolled and redraw the lines from that direction. This gives a
satisfying sense of scrolling in the proper direction.
To further improve performance, DrawView checks to see if the cursor
has moved out of or into the visible region. If it has not, only the
previous item and the current item are redrawn. This cuts drawing
time in half.
I decided to implement the scroll bar fairly early in development.
Browsing a large list can be disorienting. People need to know where
they are in a list and how much progress they are making with each
keystroke. They use the cursor keys differently in large lists than
they do in small lists so constant visual feedback is very valuable.
AREAS FOR IMPROVEMENT
Speed is not as snappy as it ought to be. Limited redraws take about
0.8 seconds, full redraws take 1.5 seconds. The code needs to run
about four times faster. An assembly language version would improve
this. Since my Saturn assembly skills are a bit rusty, I will leave
this to others for the time being.
I have not tried bit-wise scrolling of the list pane. It is
conceivable that this might be faster than re-drawing the entire pane
on some occasions.
The scroll bar is a bit cheesy in that it is horizontal and the list
is displayed vertically. This was a practical necessity since there
isn't enough vertical resolution to do it vertically. Ideally, I
would do a vertical bit-mapped scroll bar. This would have enough
resolution (48 pixels) for the job. Again, assembly language might be
the only practical solution.
Originally, I planned to take a menu key list and display it so you
could add user-defined features to the browser, but this was torpedoed
by HP's method of handling menus. There is no supported way to
display a menu and retain program control. A program must halt for the
menu to appear. In short, I can't display your menu and give you back
keycodes when the user presses menu keys. To further exacerbate matters, I can't get rid of the potentially confusing menus already displayed or use the extra line for more items. Ideas, anyone?
I may do some experimentation with binary searches on ordered lists.
This would permit an 'alpha mode' for the browser so users could press
a letter key and instantly jump to that part of the list.
Some applications may want several items from a single browse. Features to permit selection of multiple items (by creating contiguous or non-contiguous selections) before accepting would be useful if user
interface issues could be resolved.
Features for range selection and Cut/Copy/Paste would be great for
interactive list editing. This would be a dream for editing programs.
BROWSER COMMENTED LISTING
/* browse L2: msg | {msg c brOS fmtP} L1: list | name of list */
\<<
"by Jeff Brown (612) 646-2478" DROP
"v1.1" DROP
DUP
IF TYPE 6 == THEN /* if it is a name, get its value */
EVAL
END
DUP /* list */
SIZE /* listL */
DUP 5 MIN /* brL */
DUP2
158 CHR /* loop to build thumb */
\-> l b t
\<<
""
1
b l / 22 * 0 RND
FOR i
t +
NEXT
\>> /* thumb */
"\<---------------------\->" /* scrB */
6 ROLL DUP
IF TYPE 5 ==
THEN LIST\-> DROP /* msg c brOS fmtP */
ELSE 1 0 \<< \>> {}
END
3 PICK /* p */
\<<220 0.125 BEEP\>> /* eBeep */
134 CHR /* cursChr */
1 /* full */
1 /* continue */
1 /* j */
CLLCD
\-> list listL brL thumb scrB msg c brOS fmtP p eBeep cursChr